/***************************************************************************************************
 *Copyright(C),2010-2016,Sumscope
 *FileName	:  string_page.cpp
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2017/05/27
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#include "string_page.h"

#include <string.h>

#include <list>
#include <vector>

#include "../allocator/allocator_pool_manager.h"

#define RTL_FIELD_SIZE(type, field) sizeof(reinterpret_cast<type*>(0)->field)
#define BASSERTS  //;

namespace qb {
namespace base {

namespace sp {
class SStringItem {
  typedef StringPage::Element Element;
  typedef StringPage::ConstElementPtr ConstElementPtr;

 private:
  unsigned short m_magic;  // 固定的值，便于纠错和调试
  unsigned short m_id;     // id，序号
  unsigned short m_len;    // 实际字符串的长度
  unsigned short m_bytes;  // 当前类所占据的字节长度
  SStringItem* m_next;     //
  Element m_string[4];     // [m_bytes/4]+4 length
 public:
  const Element* GetString() const { return m_string; }
  static int PredicateSize(int len) {
    // 预估len个字符长度的字符串所需要占据的空间大小，包括SStringItem大小
    // 另外要考虑4字节对齐
    int thissize = sizeof(SStringItem) - RTL_FIELD_SIZE(SStringItem, m_string);
    int num = len * sizeof(Element) / 4;
    int size = num * 4 + 4;
    size = size + thissize;
    BASSERTS(size > len, "");
    return size;
  }
  void SetNext(SStringItem* next) { m_next = next; }
  SStringItem* GetNext() const { return m_next; }
  void Construct(ConstElementPtr str, int len, int size, int id) {
    // size 是字节数，len是字符数
    m_magic = StringPage::MAGIC;
    m_id = id;
    m_len = len;
    m_bytes = size;
    m_next = nullptr;
    // memset(this, 0, size); //
    // 这行是多余的，SStringBlock构建的时候就全部设为0了
    memcpy(m_string, str, len * sizeof(Element));
    m_string[len] = Element(0);
  }
  void Check(int id, SStringItem* next) {
    BASSERTS(m_magic == StringPage::MAGIC, "");
    BASSERTS(m_id == id, "");
    BASSERTS(m_len == sizeof(Element) == 1 ? strlen((char*)m_string)
                                           : wcslen((wchar_t*)m_string),
             "");
    BASSERTS(m_next == next, "");
    BASSERTS((char*)this + m_bytes == (char*)next, "");
  }
};
class SStringBlock {
  typedef StringPage::Element Element;
  typedef StringPage::ConstElementPtr ConstElementPtr;
  int m_capcity;  // m_buffer 实际所分配的字节数
  int m_really_used;  // 该block所拥有的字符串的总的长度，不包括末尾nullptr
  int m_used;  // 下一个SStringItem的开头指针相对于m_buffer的偏移量
  int m_count;             // 当前拥有的SStringItem的个数
  Element* m_buffer;       // 实际分配的内存,由外部负责处理
  SStringItem* m_current;  // 记录最近一次加入的SStringItem指针
 public:
  SStringBlock(Element* buffer, int capcity)
      : m_buffer(buffer),
        m_capcity(capcity),
        m_used(0),
        m_really_used(0),
        m_count(0),
        m_current(nullptr) {
    BASSERTS(capcity >= 1024,
             "SStringBlock.m_capcity can not be smaller than 1024");
    memset(m_buffer, 0, capcity * sizeof(Element));
  }
  ~SStringBlock() { m_buffer = nullptr; }
  void Reset() {
    m_used = m_really_used = 0;
    m_count = 0;
    m_current = nullptr;
    memset(m_buffer, 0, m_capcity * sizeof(Element));
  }
  ConstElementPtr AddString(ConstElementPtr str, int len, int size) {
    BASSERTS(m_buffer != nullptr, "");
    BASSERTS(m_capcity > 0 && m_used >= 0 && m_used <= m_capcity, "");
    if (m_used + size > m_capcity) return nullptr;

    // m_used 是字节数
    Element* ptr = (Element*)((char*)m_buffer + m_used);
    SStringItem* item = (SStringItem*)(ptr);
    item->Construct(str, len, size, m_count);

    BASSERTS(len < size, "");

    if (m_current) m_current->SetNext(item);

    m_current = item;
    m_count++;
    m_used += size;
    m_really_used += len * sizeof(Element);

    // DVIEW("Add String:item:%x old:%s str:%s used:%d
    // count:%d",ptr,str,item->GetString(),m_used,size);
    return item->GetString();
  }
  void Check() {
    SStringItem* item = (SStringItem*)m_buffer;
    SStringItem* next = nullptr;
    for (int i = 0; i < m_count; i++) {
      next = item->GetNext();
      item->Check(i, next);
      item = next;
    }
  }
};
class SStringBlockList {
  int m_block_size;
  std::vector<SStringBlock*> m_blocks;     // address increase
  std::list<SStringBlock*> m_free_blocks;  //
  HeapAllocator m_heap;

 public:
  SStringBlockList(int blockSize)
      : m_block_size(blockSize), m_heap("StringPage") {}
  ~SStringBlockList() {
    // MEM_METRIC("~SStringBlockList,count:%d,size:%d",m_blocks.size()+m_free_blocks.size(),m_block_size);
    Clear();
  }
  void DumpStatus() {
    // BLOGDEBUG("[MEMORY]free:%6d  cur:%6d block_size:%6d",
    // m_free_blocks.size(), m_blocks.size(), m_block_size);
  }
  void Reset() {
    int count = m_blocks.size();
    for (int i = 0; i < count; i++) {
      SStringBlock* block = m_blocks.at(i);
      m_free_blocks.push_back(block);
    }
    m_blocks.clear();
  }
  void Clear() {
    Reset();
    for (auto it = m_free_blocks.begin(); it != m_free_blocks.end(); ++it) {
      SStringBlock* block = *it;
      m_heap.Free(block, 0);
    }
    m_free_blocks.clear();
    m_heap.Reset();
  }
  int GetBytes() const { return m_blocks.size() * m_block_size; }
  SStringBlock* GetLastBlock() {
    if (m_blocks.size() > 0) return m_blocks.at(m_blocks.size() - 1);
    return nullptr;
  }
  SStringBlock* GetNewBlock() {
    SStringBlock* block = nullptr;
    if (!m_free_blocks.empty()) {
      block = m_free_blocks.front();
      m_free_blocks.pop_front();
      block->Reset();
    } else {
      int capcity = m_block_size * sizeof(StringPage::Element);
      StringPage::Element* buffer = (StringPage::Element*)m_heap.Alloc(capcity);
      block = new (m_heap.Alloc(sizeof(SStringBlock)))
          SStringBlock(buffer, m_block_size);
    }
    m_blocks.push_back(block);
    return block;
  }
};
}  // namespace sp

StringPage::StringPage() : m_block_size(4096 * 4), m_heap(nullptr) {
  m_block_list = new SStringBlockList(m_block_size);
}

StringPage::~StringPage() {
  Clear();
  if (m_block_list) delete m_block_list;
}

void StringPage::Reset() {
  if (m_block_list) m_block_list->Reset();
}
void StringPage::Clear() {
  if (m_block_list) m_block_list->Clear();
}
qb::base::StringPage::ConstElementPtr StringPage::AddString(ConstElementPtr str,
                                                            int len) {
  BASSERTS(str != nullptr, "");
  BASSERTS(
      len < 1024,
      "SStringBuf Can not support string whose size is larger than 1024,%d",
      len);

  SStringBlock* block = m_block_list->GetLastBlock();
  int item_size = sp::SStringItem::PredicateSize(len);
  ConstElementPtr str_add =
      block ? block->AddString(str, len, item_size) : nullptr;
  if (str_add == nullptr) {
    block = m_block_list->GetNewBlock();
    str_add = block->AddString(str, len, item_size);
  }

  BASSERTS(str_add != nullptr, "");
  return str_add;
}

int StringPage::GetBytes() { return m_block_list->GetBytes(); }
void StringPage::DumpStatus() {
  if (m_block_list) m_block_list->DumpStatus();
}
}  // namespace base
}  // namespace qb